import * as step from '@flow-step/step-toolkit'
import * as path from 'path'
import {MergeResult, simpleGit, SimpleGit} from 'simple-git'
import {SetSSHPrivateKey} from "./sshKey"
import process from 'process'
import {masking} from './util'
import {deleteBranchWithFilter} from './deleteBranchWithFilter'

const SEP = path.sep;

export async function gitMerge(){
    let projectDir = process.env.PROJECT_DIR as string;
    let repoDirName = path.basename(projectDir);  //the repo we work on
    SetSSHPrivateKey(repoDirName);

    process.env.GIT_SSL_NO_VERIFY = '1';
    const git: SimpleGit = simpleGit();
    git.env(process.env);
    await git.addConfig('user.name','rdc')
    await git.addConfig('user.email','rdc@alibaba-inc.com')
    await git.cwd(process.env.PROJECT_DIR as string);

    const allBranches = await git.branch()
    const currentBranch = allBranches.current;

    step.info(`===============================Merge Begins======================================`)
    try {
        await checkoutCode(git);
    }catch(error){
        step.error('failed to do checkout')
        throw error;
    }

    try {
        await mergeCode(git,currentBranch);
    }catch(error:any){
        if(error.message.includes('CONFLICTS')){
            process.exit(1)
        }
        step.error('failed to do mergeCode')
        throw error;
    }

    try {
        await pushCode(git);
    }catch(error){
        step.error('failed to do push')
        throw error;
    }

    step.info(`checkout back to the original branch ${currentBranch}`);
    step.info(`git checkout ${currentBranch}`)
    await git.checkout(currentBranch);

    await deleteOriginBranch(git,currentBranch);
    await updateAoneFlowContext(git);

}

export async function checkoutCode(git:SimpleGit){
    if (process.env.CLEAN_CACHE == 'true') {
        step.info('git clean -dfX')
        await git.clean(['-dfX']);
        step.info('git reset --hard HEAD')
        await git.reset(['--hard', 'HEAD']);
    }

    const branches = await git.branchLocal();
    const targetBranch = process.env.TARGET_BRANCH as string;
    try {
        if (branches.all.includes(targetBranch)) {
            step.info('git checkout ' + targetBranch)
            await git.checkout(targetBranch);
            await git.pull();
        } else {
            step.info('git checkout origin/' + targetBranch + ' -b ' + targetBranch)
            await git.checkout(['origin/' + targetBranch, '-b', targetBranch]);
            await git.pull();
        }
    }catch (error:any){
        if (error.message.includes('did not match any')){
            step.error(`Target branch ${targetBranch} does not exist, available branches:` )
            const allBranches = await git.branch()
            step.info(allBranches.all.toString())
        }
        throw error
    }
}

export async function mergeCode(git:SimpleGit,currentBranch:string){
    step.info(`git merge ${currentBranch}`)
    let mergeRes: MergeResult | undefined = undefined;
    try {
        mergeRes = await git.merge([currentBranch]);
    }catch (error:any){
        if (error.message.includes('not something we can merge')){
            step.interaction.appendErrorMessage('branch.not.exist')
            step.error(`Branch ${currentBranch} does not exist, available branches:` )
            let allBranches = await git.branch();
            step.info(allBranches.all.toString())
        }
        if (error.message.includes('CONFLICTS')){
            step.error(`Merge conflict, please try merging origin/${process.env.TARGET_BRANCH} to ${currentBranch} and pushing again.` )
            step.info(`==============================How to Solve Conflict==============================` )
            step.info(`# After resolving conflicts and commiting code, please rerun the pipeline` )
            step.info(`git fetch origin` )
            step.info(`git checkout -b ${currentBranch} origin/${currentBranch}` )
            step.info(`git merge origin/${process.env.TARGET_BRANCH}` )
            step.info(`=================================================================================` )
        }
        throw error;
    }
}


export async function pushCode(git:SimpleGit){
    step.info('git push origin ' + process.env.TARGET_BRANCH)
    await git.push('origin',process.env.TARGET_BRANCH,['-o', 'review=no'])
        .catch(async () => {
            try {
                return await git.push('origin', process.env.TARGET_BRANCH, ['-o'])
            } catch (error:any) {
                if(error.message.includes('not found')){
                    step.interaction.appendErrorMessage('repo.not.exist')
                    step.error(`repository does not exist, or the user ${process.env.GIT_USERNAME} has no permission`)
                }
                throw new Error(masking(error.message));
            }
        });
}

export async function deleteOriginBranch(git:SimpleGit,currentBranch:string){
    if (process.env.DELETE_ORIGIN_BRANCH != 'true') return;
    await deleteBranchWithFilter(git,currentBranch)
}

export async function updateAoneFlowContext(git:SimpleGit){
    if (process.env.branchRepoInfo != undefined && process.env.branchRepoInfo != ''&& process.env.branchRepoInfo != '[]'){
        let parsedBranchInfo = JSON.parse(process.env.branchRepoInfo);
        let workDir = parsedBranchInfo[0].name;
        if (workDir === undefined || workDir === ''){
            workDir = parsedBranchInfo[0].sign;
        }
        step.info(`keepIntegrationAreaBranch ${process.env.keepIntegrationAreaBranch}`)

        if (process.env.PROJECT_DIR == `${process.env.WORK_SPACE}/${workDir}` && "true" != process.env.keepIntegrationAreaBranch){
            let aoneFlowMaster = parsedBranchInfo[0].baseBranch.branchName;
            step.info(`Branch Mode - the current workspace is associated with branch mode repository.`)
            if (process.env.TARGET_BRANCH == aoneFlowMaster){
                step.info(`Branch Mode - The trunk of Branch Mode '${aoneFlowMaster}' is the same as the target branch '${process.env.TARGET_BRANCH}'. Automatically remove branches from the integration area that have been merged into the trunk.`);
                let repoURL = parsedBranchInfo[0].repo;
                const featureBranches = parsedBranchInfo[0].featureBranchs || [];
                let commitInfoArray: string[] = [];
                for (let branch of featureBranches) {
                    commitInfoArray.push(`${branch.branchName}:${branch.commitId}:MERGED`);
                }
                const commitInfo = commitInfoArray.join(';');
                step.info(`Branch Mode - Branch status: ${repoURL}.COMMIT_INFO=${commitInfo}`)
                step.addOutput(`${repoURL}.COMMIT_INFO=${commitInfo}`)
                if (process.env.DELETE_ORIGIN_BRANCH == 'true'){
                    step.info(`Branch Mode - clean feature branches.`)
                    for (let branch of featureBranches) {
                        let branchName = branch.branchName;
                        await deleteBranchWithFilter(git,branchName);
                    }
                }
            }
        }
    }
}
